package com.nulldev.util.JVM.reflections.cache;

import java.lang.reflect.Field;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.internal.backport.arrays.BackportArrays;

final class ClassElement {
	private Class<?> clazz = null;
	private String superClazz = null;
	private String[] interfaze = null;

	private final AtomicReference<FieldObject> fields = new AtomicReference<FieldObject>();
	private final AtomicReference<ConstructorObject> constructors = new AtomicReference<ConstructorObject>();
	private final AtomicReference<MethodObject> methods = new AtomicReference<MethodObject>();

	protected ClassElement(Class<?> clazz) {
		try {
			this.clazz = clazz;
			this.superClazz = getSuperClazz(clazz);
			this.interfaze = getInterface(clazz);
		} catch (Exception e) {
			throw new CachedReflectException(e);
		}
	}

	private static final String getSuperClazz(Class<?> clazz) {
		Class<?> c = clazz.getSuperclass();
		if (c == null) {
			return null;
		}
		return c.getName();
	}

	private static final String[] getInterface(Class<?> clazz) {
		Class<?>[] cz = clazz.getInterfaces();
		if (cz == null) {
			return null;
		}
		int len = cz.length;
		if (len > 0) {
			String[] ret = new String[len];
			for (int i = 0; i < len; i++) {
				ret[i] = cz[i].getName();
			}
			BackportArrays.sort(ret);
			return ret;
		}
		return null;
	}

	public Class<?> getClassObject() {
		return clazz;
	}

	public String getSuperClassName() {
		return superClazz;
	}

	public String[] getInterfaseNames() {
		return interfaze;
	}

	public final Object newInstance(Class<?>[] types, Object[] args, ClassLoader cl) {
		ConstructorObject c = constructors.get();
		if (c == null) {
			c = new ConstructorObject(clazz);
			while (!constructors.compareAndSet(constructors.get(), c))
				;
		}
		return c.newInstance(cl, types, args);
	}

	public final boolean isField(boolean staticFlag, String name) {
		FieldObject f = fields.get();
		if (f == null) {
			f = new FieldObject(clazz);
			while (!fields.compareAndSet(fields.get(), f))
				;
		}
		return f.isField(staticFlag, name);
	}

	public final void fieldNames(Set<String> out, boolean staticFlag) {
		FieldObject f = fields.get();
		if (f == null) {
			f = new FieldObject(clazz);
			while (!fields.compareAndSet(fields.get(), f))
				;
		}
		f.getNames(out, staticFlag);
	}

	public final Field getField(boolean staticFlag, String name) {
		FieldObject f = fields.get();
		if (f == null) {
			f = new FieldObject(clazz);
			while (!fields.compareAndSet(fields.get(), f))
				;
		}
		return f.get(staticFlag, name);
	}

	public final boolean isMethod(boolean staticFlag, String name) {
		MethodObject m = methods.get();
		if (m == null) {
			m = new MethodObject(clazz);
			while (!methods.compareAndSet(methods.get(), m))
				;
		}
		return m.isMethod(staticFlag, name);
	}

	public final void methodNames(Set<String> out, boolean staticFlag) {
		MethodObject m = methods.get();
		if (m == null) {
			m = new MethodObject(clazz);
			while (!methods.compareAndSet(methods.get(), m))
				;
		}
		m.getNames(out, staticFlag);
	}

	public final boolean invoke(Object[] result, Object target, String name, ClassLoader cl, Class<?>[] types, Object[] args) {
		MethodObject m = methods.get();
		if (m == null) {
			m = new MethodObject(clazz);
			while (!methods.compareAndSet(methods.get(), m))
				;
		}
		return m.invoke(result, target, name, cl, types, args);
	}
}
